/**
 * 排序序列生成器
 * @param  {Integer} multiple 生成序列的默认位数倍数
 */
const OrderSequence = function(multiple) {
	// 设置的位数必须是数字
	if (typeof multiple != 'number') {
		throw new Error(`multiple type must be a number.`);
	}
	// 设置的位数必须大于等于1
	if (multiple < 1) {
		throw new Error(`multiple type must be greater or equal to 1.`);
	}
	/*
	进制。
	因为每次获取新的行号都取中值，所以进制是2的倍数最合适。
	而32进制是JavaScript是默认支持的2的倍数的最大进制。
	 */
	const _ary = 32;
	/*
	32进制下，最大的数字
	 */
	const _maxNumber = 'v';
	/*
	超过10位的32进制会丢失精度
	 */
	const _saftDigit = 10;
	const _digitMultiple = Math.floor(multiple);
	const _maxSaftValue = parseInt(new Array(_saftDigit).fill(_maxNumber).join(''), _ary);
	/*
	位数。
	 */
	const _digit = _digitMultiple * _saftDigit;

	this.getMinValue = () => {
		let _minValue = [0];
		for (var i = 0; i < _digitMultiple; i++) {
			_minValue.push(0);
		}
		return _minValue;
	};

	this.getMaxValue = () => {
		let _maxValue = [1];
		for (var i = 0; i < _digitMultiple; i++) {
			_maxValue.push(0);
		}
		return _maxValue;
	};

	console.log('ary', _ary);
	console.log('digit', _digit);
	console.log('digit multiple', _digitMultiple);
	console.log('max safe value', _maxSaftValue);
	console.log('min value', this.getMinValue().join(','));
	console.log('max value', this.getMaxValue().join(','));

	/**
	 * 将字符串转换为大数集合
	 * @param  {String} numberString 需要转换的字符串
	 * @return {Array}              从右向左每10位进行一次拆分
	 */
	this.covertBigNumber = (numberString) => {
		if (typeof numberString != 'string' || numberString.length != _digit) {
			return null;
		}
		// 补齐溢出位
		let bigNumber = [0];
		for (var i = 0; i < _digitMultiple; i++) {
			let startIndex = i * _saftDigit;
			let convertValue = parseInt(numberString.substr(startIndex, _saftDigit), _ary);
			if (isNaN(convertValue)) {
				return null;
			}
			bigNumber.push(convertValue);
		}


		return bigNumber;
	};

	/**
	 * 大数比较
	 * @param  {[type]} bigNumberA [description]
	 * @param  {[type]} bigNumberB [description]
	 * @return {[type]}            [description]
	 */
	this.compareBigNumber = (bigNumberA, bigNumberB) => {
		for (var i = 0; i < bigNumberA.length; i++) {
			let result = bigNumberA[i] - bigNumberB[i];
			if (result > 0) {
				return 1;
			} else if (result < 0) {
				return -1;
			}
		}
		return 0;
	};

	/**
	 * 大数加法
	 * @param  {[type]} bigNumberA [description]
	 * @param  {[type]} bigNumberB [description]
	 * @return {[type]}            [description]
	 */
	this.additionBigNumber = (bigNumberA, bigNumberB) => {
		let bigResult = new Array(bigNumberA.length);
		// 从右相左
		for (var i = bigResult.length - 1; i >= 0; i--) {
			let numberA = bigNumberA[i];
			let numberB = bigNumberB[i];

			let	result = numberA + numberB;
			// 需要进位 并且 可以进位
			if (result > _maxSaftValue && i > 0) {
				result = result - _maxSaftValue + 1;
				bigNumberA[i - 1] = bigNumberA[i - 1] + 1;
			}
			bigResult[i] = result;
		}

		return bigResult;
	};

	/**
	 * 大数减法
	 * @param  {[type]} bigNumberA [description]
	 * @param  {[type]} bigNumberB [description]
	 * @return {[type]}            [description]
	 */
	this.minusBigNumber = (bigNumberA, bigNumberB) => {
		let bigResult = new Array(bigNumberA.length);
		// 从右相左
		for (var i = bigResult.length - 1; i >= 0; i--) {
			let numberA = bigNumberA[i];
			let numberB = bigNumberB[i];

			let	result = numberA - numberB;
			// 需要借位 并且 可以借位
			if (result < 0 && i > 0) {
				result = result + _maxSaftValue + 1;
				bigNumberA[i - 1] = bigNumberA[i - 1] - 1;
			}
			bigResult[i] = result;
		}

		return bigResult;
	};

	/**
	 * 大数整除
	 * @param  {[type]} bigNumber [description]
	 * @param  {[type]} divisor   [description]
	 * @return {[type]}           [description]
	 */
	this.divisibleBigNumber = (bigNumber, divisor) => {
		let bigResult = new Array(bigNumber.length);
		// 从左向右
		for (var i = 0; i < bigResult.length; i++) {
			let number = bigNumber[i];
			let result = Math.floor(number / divisor);
			let mod = number % divisor;
			// 有余数
			if (mod) {
				if (i + 1 < bigResult.length) { // 还有下一位
					bigNumber[i + 1] = bigNumber[i + 1] + _maxSaftValue * mod;
				} else { // 最后一位，4舍5入
					result += Math.round(mod / divisor);
				}
			}
			bigResult[i] = result;
		}
		return bigResult;
	};

	/**
	 * 大数乘以普通数
	 * @param  {[type]} bigNumber  [description]
	 * @param  {[type]} multiplier [description]
	 * @return {[type]}            [description]
	 */
	this.timesBigNumber = (bigNumber, multiplier) => {
		let bigResult = new Array(bigNumber.length);
		// 从右向左
		for (var i = bigResult.length - 1; i >= 0; i--) {
			let number = bigNumber[i];
			let result = number * multiplier;
			// 需要进位 并且 可以进位
			if (result > _maxSaftValue && i > 0) {
				let carryNum = Math.floor(result / (_maxSaftValue + 1));
				result = result % (_maxSaftValue + 1);
				bigNumber[i - 1] = bigNumber[i - 1] + carryNum;
			}
			bigResult[i] = result;
		}
		return bigResult;
	};

	this.toStringBigNumber = (bigNumber) => {
		let bigString = "";
		// 从左向右，跳过溢出位
		for (var i = 0; i < bigNumber.length; i++) {
			let number = bigNumber[i];
			// 消除溢出
			if (number > _maxSaftValue) {
				number = _maxSaftValue;
			}
			let numberString = number.toString(_ary);
			// 如果获取值的位数小于digit，则在前面补0。
			if (numberString.length < _saftDigit) {
				numberString = new Array(_saftDigit - numberString.length).fill('0').join('') + numberString;
			}
			bigString += numberString;
		}
		return bigString.toLocaleUpperCase();
	};

	this.getFractionByBigNumber = (beforeValue, afterValue, molecule, denominator) => {
		console.log('before value', beforeValue);
		console.log('after value', afterValue);
		console.log('molecule value', molecule);
		console.log('denominator value', denominator);
		// 类型错误
		if (!Array.isArray(beforeValue) || !Array.isArray(afterValue) || typeof molecule != 'number' || typeof denominator != 'number' ) {
			return null;
		}
		// 长度错误
		if (
			(beforeValue.length != _digitMultiple && beforeValue.length != _digitMultiple + 1) ||
			(afterValue.length != _digitMultiple && afterValue.length != _digitMultiple + 1)
		) {
			return null;
		}
		// 分母最大为10 或者 分子超过分母
		if (denominator > 10 || molecule >= denominator) {
			return null;
		}

		// 补充溢出位，用于对齐计算
		if (beforeValue.length != _digitMultiple + 1) {
			beforeValue.unshift(0);
		}
		if (afterValue.length != _digitMultiple + 1) {
			afterValue.unshift(0);
		}

		let beforeString = this.toStringBigNumber(beforeValue);
		let afterString = this.toStringBigNumber(afterValue);
		console.log('before string', beforeString);
		console.log('after string', afterString);

		// 没有中间值
		if (this.compareBigNumber(beforeValue, afterValue) >= 0) {
			return null;
		}
		// 计算分子数，对于无法除尽的小数，采取4舍5入。
		let divisibleResult = this.divisibleBigNumber(this.minusBigNumber(afterValue, beforeValue), denominator);
		let medianValue = this.additionBigNumber(
			beforeValue,
			this.timesBigNumber(divisibleResult, molecule)
		);
		console.log('median number', medianValue);
		// 转换为进制字符串
		medianValue = this.toStringBigNumber(medianValue);
		console.log('median string', medianValue);
		// 如果中间值大于等于当前下限，则返回null
		if (medianValue.localeCompare(afterString) >= 0 || medianValue.localeCompare(beforeString) <= 0) {
			return null;
		}
		// 输出取消溢出位的字符
		return medianValue.substr(0 - _digit);
	};

	/**
	 * 获取顶端到末尾的molecule/denominator位置的行号。返回null表示生成失败。
	 * @param  {[type]} molecule    [description]
	 * @param  {[type]} denominator [description]
	 * @return {[type]}             [description]
	 */
	this.getFractionByAll = (molecule, denominator) => {
		return this.getFractionByBigNumber(this.getMinValue(), this.getMaxValue(), molecule, denominator);
	};

	/**
	 * 获取指定行到顶端的molecule/denominator位置的行号。返回null表示生成失败。
	 * @param  {[type]} line        [description]
	 * @param  {[type]} molecule    [description]
	 * @param  {[type]} denominator [description]
	 * @return {[type]}             [description]
	 */
	this.getFractionToHead = (line, molecule, denominator) => {
		console.log('line', line);

		// 传入行号必须是String，并且位数和当前定义的位数一致
		if (typeof line != 'string' || line.length != _digit) {
			return null;
		}
		let lineValue = this.covertBigNumber(line);
		console.log('line value', lineValue);
		// 无法按照进制转换
		if (lineValue == null) {
			return null;
		}
		return this.getFractionByBigNumber(this.getMinValue(), lineValue, molecule, denominator);
	};

	/**
	 * 获取指定行到末尾的molecule/denominator位置的行号。返回null表示生成失败。
	 * @param  {[type]} line        [description]
	 * @param  {[type]} molecule    [description]
	 * @param  {[type]} denominator [description]
	 * @return {[type]}             [description]
	 */
	this.getFractionToTail = (line, molecule, denominator) => {
		console.log('line', line);

		// 传入行号必须是String，并且位数和当前定义的位数一致
		if (typeof line != 'string' || line.length != _digit) {
			return null;
		}
		let lineValue = this.covertBigNumber(line);
		console.log('line value', lineValue);
		// 无法按照进制转换
		if (lineValue == null) {
			return null;
		}
		return this.getFractionByBigNumber(lineValue, this.getMaxValue(), molecule, denominator);
	};

	/**
	 * 获取两行之间的molecule/denominator位置的行号。返回null表示生成失败。
	 * @param  {[type]} before      [description]
	 * @param  {[type]} after       [description]
	 * @param  {[type]} molecule    [description]
	 * @param  {[type]} denominator [description]
	 * @return {[type]}             [description]
	 */
	this.getFraction = (before, after, molecule, denominator) => {
		console.log('before', before);
		console.log('after', after);

		// 传入行号必须是String，并且位数和当前定义的位数一致
		if (typeof before != 'string' || before.length != _digit || typeof after != 'string' || after.length != _digit ) {
			return null;
		}

		// 没有中间值
		if (before.localeCompare(after) >= 0) {
			return null;
		}

		let beforeValue = this.covertBigNumber(before);
		console.log('before number', beforeValue);
		// 无法按照进制转换
		if (beforeValue == null) {
			return null;
		}
		let afterValue = this.covertBigNumber(after);
		console.log('after number', afterValue);
		// 无法按照进制转换
		if (afterValue == null) {
			return null;
		}
		return this.getFractionByBigNumber(beforeValue, afterValue, molecule, denominator);
	};

	/**
	 * 获取顶端到末尾的中位数行号。返回null表示生成失败
	 * @return {[type]} [description]
	 */
	this.getMedianByAll = () => {
		return this.getMedianByBigNumber(this.getMinValue(), this.getMaxValue());
	};

	/**
	 * 获取指定行到顶端的中位数行号。返回null表示生成失败
	 * @param  {[type]} line [description]
	 * @return {[type]}      [description]
	 */
	this.getMedianToHead = (line) => {
		console.log('line', line);

		// 传入行号必须是String，并且位数和当前定义的位数一致
		if (typeof line != 'string' || line.length != _digit) {
			return null;
		}
		let lineValue = this.covertBigNumber(line);
		console.log('line value', lineValue);
		// 无法按照进制转换
		if (lineValue == null) {
			return null;
		}

		return this.getMedianByBigNumber(this.getMinValue(), lineValue);
	};

	/**
	 * 获取指定行到末尾的中位数行号。返回null表示生成失败。
	 * @param  {[type]} line [description]
	 * @return {[type]}      [description]
	 */
	this.getMedianToTail = (line) => {
		console.log('line', line);

		// 传入行号必须是String，并且位数和当前定义的位数一致
		if (typeof line != 'string' || line.length != _digit) {
			return null;
		}
		let lineValue = this.covertBigNumber(line);
		console.log('line value', lineValue);
		// 无法按照进制转换
		if (lineValue == null) {
			return null;
		}

		return this.getMedianByBigNumber(lineValue, this.getMaxValue());
	};

	/**
	 * 获取两行之间的中位数行号。返回null表示生成失败。
	 *
	 * @param  {String} before 前一行行号，如果传入null、undefined等，则使用最小值。
	 * @param  {String} after  后一行行号，如果传入null、undefined等，则使用最大值。
	 * @return {String}        新生成的行号，如果前一行和后一行之间无法生成新行，返回NaN。
	 */
	this.getMedian = (before, after) => {
		console.log('before', before);
		console.log('after', after);

		// 传入行号必须是String，并且位数和当前定义的位数一致
		if (typeof before != 'string' || before.length != _digit || typeof after != 'string' || after.length != _digit ) {
			return null;
		}

		// 没有中间值
		if (before.localeCompare(after) >= 0) {
			return null;
		}

		let beforeValue = this.covertBigNumber(before);
		console.log('before number', beforeValue);
		// 无法按照进制转换
		if (beforeValue == null) {
			return null;
		}
		let afterValue = this.covertBigNumber(after);
		console.log('after number', afterValue);
		// 无法按照进制转换
		if (afterValue == null) {
			return null;
		}
		return this.getMedianByBigNumber(beforeValue, afterValue);
	};

	/**
	 * 获取两行之间的中位数行号。返回null表示生成失败。
	 * @param  {[type]} beforeValue [description]
	 * @param  {[type]} afterValue  [description]
	 * @return {[type]}             [description]
	 */
	this.getMedianByBigNumber = (beforeValue, afterValue) => {
		return this.getFractionByBigNumber(beforeValue, afterValue, 1, 2);
	};
};
